using System;
using System.Collections.Generic;
using 語法分析;

namespace 語義分析
{
    /// <summary>
    /// 語義分析器 - 進行類型檢查和語義驗證
    /// </summary>
    public class 語義分析器 : 語法樹訪問者
    {
        private 符號表 當前符號表;
        private List<string> 錯誤列表;
        
        /// <summary>
        /// 構造函數
        /// </summary>
        public 語義分析器()
        {
            當前符號表 = new 符號表();
            錯誤列表 = new List<string>();
        }
        
        /// <summary>
        /// 分析抽象語法樹
        /// </summary>
        /// <param name="程序節點">程序根節點</param>
        public void 分析(程序節點 程序節點)
        {
            錯誤列表.Clear();
            程序節點.接受訪問者(this);
            
            if (錯誤列表.Count > 0)
            {
                string 錯誤信息 = string.Join("\n", 錯誤列表);
                throw new Exception($"語義分析錯誤:\n{錯誤信息}");
            }
        }
        
        public void 訪問程序節點(程序節點 節點)
        {
            foreach (var 語句 in 節點.語句列表)
            {
                語句.接受訪問者(this);
            }
        }
        
        public void 訪問變量聲明節點(變量聲明節點 節點)
        {
            // 檢查變量是否已經聲明
            if (當前符號表.是否存在(節點.變量名稱))
            {
                添加錯誤($"變量 '{節點.變量名稱}' 已經聲明", 節點.行號, 節點.列號);
                return;
            }
            
            // 添加到符號表
            當前符號表.添加符號(節點.變量名稱, 節點.變量類型, 符號類型.變量);
            
            // 檢查初始值類型
            if (節點.初始值 != null)
            {
                節點.初始值.接受訪問者(this);
                // 這裡可以添加類型檢查邏輯
            }
        }
        
        public void 訪問函數定義節點(函數定義節點 節點)
        {
            // 檢查函數是否已經定義
            if (當前符號表.是否存在(節點.函數名稱))
            {
                添加錯誤($"函數 '{節點.函數名稱}' 已經定義", 節點.行號, 節點.列號);
                return;
            }
            
            // 添加函數到符號表
            當前符號表.添加符號(節點.函數名稱, "函數", 符號類型.函數);
            
            // 創建新的作用域
            當前符號表.進入作用域();
            
            // 添加參數到符號表
            foreach (var 參數 in 節點.參數列表)
            {
                參數.接受訪問者(this);
            }
            
            // 分析函數體
            foreach (var 語句 in 節點.函數體)
            {
                語句.接受訪問者(this);
            }
            
            // 退出作用域
            當前符號表.退出作用域();
        }
        
        public void 訪問參數節點(參數節點 節點)
        {
            // 檢查參數是否重複
            if (當前符號表.是否存在(節點.參數名稱))
            {
                添加錯誤($"參數 '{節點.參數名稱}' 重複定義", 節點.行號, 節點.列號);
                return;
            }
            
            當前符號表.添加符號(節點.參數名稱, 節點.參數類型, 符號類型.參數);
        }
        
        public void 訪問條件語句節點(條件語句節點 節點)
        {
            // 分析條件表達式
            節點.條件表達式.接受訪問者(this);
            
            // 分析真分支
            foreach (var 語句 in 節點.真分支)
            {
                語句.接受訪問者(this);
            }
            
            // 分析假分支
            foreach (var 語句 in 節點.假分支)
            {
                語句.接受訪問者(this);
            }
        }
        
        public void 訪問循環語句節點(循環語句節點 節點)
        {
            // 分析循環體
            foreach (var 語句 in 節點.循環體)
            {
                語句.接受訪問者(this);
            }
        }
        
        public void 訪問輸出語句節點(輸出語句節點 節點)
        {
            節點.輸出表達式.接受訪問者(this);
        }
        
        public void 訪問返回語句節點(返回語句節點 節點)
        {
            if (節點.返回值 != null)
            {
                節點.返回值.接受訪問者(this);
            }
        }
        
        public void 訪問賦值語句節點(賦值語句節點 節點)
        {
            // 檢查變量是否已聲明
            if (!當前符號表.是否存在(節點.變量名稱))
            {
                添加錯誤($"變量 '{節點.變量名稱}' 未聲明", 節點.行號, 節點.列號);
                return;
            }
            
            // 分析賦值表達式
            節點.賦值表達式.接受訪問者(this);
        }
        
        public void 訪問二元運算表達式節點(二元運算表達式節點 節點)
        {
            節點.左操作數.接受訪問者(this);
            節點.右操作數.接受訪問者(this);
            
            // 這裡可以添加運算符類型檢查邏輯
        }
        
        public void 訪問字面量表達式節點(字面量表達式節點 節點)
        {
            // 字面量不需要特殊處理
        }
        
        public void 訪問標識符表達式節點(標識符表達式節點 節點)
        {
            // 檢查標識符是否已聲明
            if (!當前符號表.是否存在(節點.名稱))
            {
                添加錯誤($"標識符 '{節點.名稱}' 未聲明", 節點.行號, 節點.列號);
            }
        }
        
        public void 訪問函數調用表達式節點(函數調用表達式節點 節點)
        {
            // 檢查函數是否已定義
            if (!當前符號表.是否存在(節點.函數名稱))
            {
                添加錯誤($"函數 '{節點.函數名稱}' 未定義", 節點.行號, 節點.列號);
                return;
            }
            
            // 分析參數表達式
            foreach (var 參數 in 節點.參數列表)
            {
                參數.接受訪問者(this);
            }
        }
        
        /// <summary>
        /// 添加錯誤信息
        /// </summary>
        /// <param name="消息">錯誤消息</param>
        /// <param name="行號">行號</param>
        /// <param name="列號">列號</param>
        private void 添加錯誤(string 消息, int 行號, int 列號)
        {
            錯誤列表.Add($"第 {行號} 行第 {列號} 列: {消息}");
        }
    }
} 